package com.example.redis.service;

import cn.hutool.json.JSONUtil;
import com.example.redis.annotation.RedisDistributedLock;
import com.example.redis.dao.TestDemoDao;
import com.example.redis.entity.TestDemo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class RedisDistributedLockService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private TestDemoDao testDemoDao;

    @RedisDistributedLock(lockField = {"name", "city"}, lockExpireTime = 10)
    public TestDemo insert(TestDemo vo) {
        TestDemo dto = null;
        // 先查缓存、缓存存在就直接返回
        String key = vo.getName() + vo.getCity();

        String json = redisTemplate.opsForValue().get(key);
        if (!StringUtils.isEmpty(json)) {
            dto = JSONUtil.toBean(json, TestDemo.class);
            log.info("key: {} 取缓存直接返回", key);
            return dto;
        }

        // 查数据库
        dto = testDemoDao.findByNameAndCity(vo.getName(), vo.getCity());
        if (dto != null) {

            log.info("key: {} 数据库存在直接返回", key);
            // 刷新缓存
            redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(dto), 1, TimeUnit.MINUTES);
            return dto;
        }

        // 缓存不存在就新增
        String code = UUID.randomUUID().toString();
        log.info("生成的code: {}", code);
        vo.setCode(code);
        vo.setCreateTime(new Date());

        dto = testDemoDao.save(vo);
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(dto), 1, TimeUnit.MINUTES);
        log.info("key: {} 新增成功", key);

        return dto;
    }

    public TestDemo insert2(TestDemo vo) {
        TestDemo dto = null;
        // 先查缓存、缓存存在就直接返回
        String key = vo.getName() + vo.getCity();

        String json = redisTemplate.opsForValue().get(key);
        if (!StringUtils.isEmpty(json)) {
            dto = JSONUtil.toBean(json, TestDemo.class);
            log.info("key: {} 取缓存直接返回", key);
            return dto;
        }

        // 查数据库
        dto = testDemoDao.findByNameAndCity(vo.getName(), vo.getCity());
        if (dto != null) {

            log.info("key: {} 数据库存在直接返回", key);
            // 刷新缓存
            redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(dto), 1, TimeUnit.MINUTES);
            return dto;
        }

        // 缓存不存在就新增
        String code = UUID.randomUUID().toString();
        log.info("生成的code: {}", code);
        vo.setCode(code);
        vo.setCreateTime(new Date());

        dto = testDemoDao.save(vo);
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(dto), 1, TimeUnit.MINUTES);
        log.info("key: {} 新增成功", key);

        return dto;
    }

    @RedisDistributedLock(lockField = {"name"}, lockExpireTime = 10)
    public String testLockByField(String name) {
        // 模拟数据库插入
        String key = "testLockByField:" + name + System.currentTimeMillis();
        log.info("key: {}", key);
        redisTemplate.opsForValue().set(key, name + ":测试单个参数");
        log.info("模拟数据入库成功...");

        return name;
    }

    @RedisDistributedLock(lockField = {"name"}, lockExpireTime = 10)
    public String testLockByField2(String name, String city) {
        // 模拟数据库插入
        String key = "testLockByField2:" + name + System.currentTimeMillis();
        log.info("key: {}", key);
        redisTemplate.opsForValue().set(key, name + ":测试多个参数只取一个参数作为锁字段");
        log.info("模拟数据入库成功...");

        return name;
    }

    @RedisDistributedLock(lockField = {"name", "age"}, lockExpireTime = 10)
    public String testLockByField3(String name, String city, int age) {
        // 模拟数据库插入
        String key = "testLockByField3:" + name + age + System.currentTimeMillis();
        log.info("key: {}", key);
        redisTemplate.opsForValue().set("testLockByField3:" + name + age, name + ":测试多个参数取多个参数作为锁字段");
        log.info("模拟数据入库成功...");

        return name;
    }

    @RedisDistributedLock(lockField = {"name", "city"}, lockExpireTime = 10)
    public TestDemo testLockByEntity(TestDemo demo) {
        // 模拟数据库插入
        String key = "testLockByEntity:" + demo.getName() + demo.getCity() + System.currentTimeMillis();
        log.info("key: {}", key);
        redisTemplate.opsForValue().set(key, "测试一个实体类为入参取多参数组合作为锁字段");
        log.info("模拟数据入库成功...");

        return demo;
    }
}
